#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "jit_assert.hpp"
#include "hir_instructions.hpp"
#include "hir_compiler.hpp"


static void do_compile(const char * code,Py_ssize_t len,PyObject * co_consts,PyObject * co_names,PyObject * co_varnames,PyObject *co_cellvars,PyObject * co_freevars,PyCodeObject * codeObject);
#define GET_ATTR(o,name)\
    name = PyObject_GetAttrString(o,#name)

static PyObject *
hir_compile(PyObject *self, PyObject *args)
{
    PyObject * func;
    PyObject * func_code;
    PyObject * co_consts;
    PyObject * co_names;
    PyObject * co_varnames;
    PyObject * co_cellvars;
    PyObject * co_freevars;
    PyObject * co_code;


    if (!PyArg_ParseTuple(args, "O", &func))
    {
        fprintf(stderr,"you need to supply object!\n");
        return NULL;
    }
    // if(!PyCode_Check(func_code))
    // {
    //     fprintf(stderr,"you need to supply code object!\n");
    //     return NULL;
    // }

    // get func_code
    GET_ATTR(func,func_code);
    if(!func_code)
    {
        fprintf(stderr,"need func_code attr but does not exist\n");
        return NULL;
    }
    // get attr
    GET_ATTR(func_code,co_consts);
    GET_ATTR(func_code,co_names);
    GET_ATTR(func_code,co_varnames);
    GET_ATTR(func_code,co_cellvars);
    GET_ATTR(func_code,co_freevars);
    GET_ATTR(func_code,co_code);
    Py_DECREF(func_code);
    // check if not exist
    if(!(co_consts && co_names && co_varnames && co_cellvars && co_freevars && co_code))
    {
        Py_XDECREF(co_consts);
        Py_XDECREF(co_names);
        Py_XDECREF(co_varnames);
        Py_XDECREF(co_cellvars);
        Py_XDECREF(co_freevars);
        Py_XDECREF(co_code);
        fprintf(stderr,"some of the attr not exists %p %p %p %p %p %p\n",co_consts,co_names,co_varnames,co_cellvars,co_freevars,co_code);
        return NULL;
    }
    do
    {
        // check if co code is a string
        if(!PyString_Check(co_code))
        {
            break;
        }
        Py_ssize_t len =  PyString_Size(co_code);
        if(!len)
        {
            // why do we need a empty code for
            break;
        }

        const char * codeString = PyString_AsString(co_code);
        if(!codeString)
        {
            // why do we need a empty code for
            break;
        }
        do_compile(codeString,len,co_consts,co_names,co_varnames,co_cellvars,co_freevars,(PyCodeObject*)func_code);
    }
    while(0);


    Py_DECREF(co_consts);
    Py_DECREF(co_names);
    Py_DECREF(co_varnames);
    Py_DECREF(co_cellvars);
    Py_DECREF(co_freevars);
    Py_DECREF(co_code);
    return Py_None;
}

static PyMethodDef TestHirMethods[] =
{
    {
        "compile",  hir_compile, METH_VARARGS,
        "test-hir dis"
    },
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

PyMODINIT_FUNC
__attribute__((visibility("default")))initTestHir(void)
{
    PyObject *m;

    m = Py_InitModule("TestHir", TestHirMethods);
    if (m == NULL)
        return;

    // SpamError = PyErr_NewException("hir.error", NULL, NULL);
    // Py_INCREF(SpamError);
    // PyModule_AddObject(m, "error", SpamError);
}



static void do_compile(const char * code,Py_ssize_t len,PyObject * co_consts,PyObject * co_names,PyObject * co_varnames,PyObject *co_cellvars,PyObject * co_freevars,PyCodeObject * codeObject)
{
    char * mycode = (char *)malloc(len + 1);
    memcpy(mycode,code,len);
    mycode[len] = 0;
    printf("code = %s\n",mycode);
    free(mycode);
    hir::CompileDesc desc = { (unsigned char const *)code,(size_t)len,co_consts,co_names,co_varnames,co_cellvars,co_freevars,codeObject};
    hir::Compiler compiler(desc);
    compiler.compile();
}

